package process;

import memory.Memory;
import memory.MemoryDispatcher;
import memory.RecordBlock;

import util.ExecutionFileGenerator;

import java.util.ArrayList;
import java.util.Map;
import java.util.Random;

public class Process {
    private PCB pcb;
    private int size;//进出需要执行的指令条数
//    内存分配器
    private static MemoryDispatcher memoryDispatcher = MemoryDispatcher.getInstance();
//    指令list
    private static String[] instructionlist = new String[]{"x=", "x++", "x--", "!"};
//    设备list
    private static String[] devicelist = new String[]{"A", "B", "C"};

//    创建进程   传入 执行指令list   PCB名字
//    启动时会自动创建一个闲逛进程
    public static Process createProcess(String[] instructions, String name) {
//        创建一个进程
        Process proc = new Process();
//        指令长度就是进程长度
        proc.size = instructions.length;
//        向新建进程pcb，设置pcb名字
        proc.pcb = MemoryDispatcher.applyPCB(name);
        if (proc.pcb == null) {
            System.out.println("进程过多或该进程名已被注册");
            return null;
        } else {
//            用执行指令list去找到变量，设置进出pcb中的变量表
            setVariableArea(instructions, proc.pcb.getVariableArea());
            int testaddress;
//            初始化时会创建一个闲逛进程，如果是闲逛进程
            if (name.equals("闲逛进程")) {
                testaddress = 49;
            } else {
//                正常线程，传入指令条数，从内存找出空白地址
                testaddress = memoryDispatcher.findBlankAddress(instructions.length);
            }
            if (testaddress == -1) {
                System.out.println("内存空间不足");
                return null;
            } else {
//                设置pcb指令执行起始点
                proc.pcb.setStartpointer(testaddress);
//                设置pcb程序计数器执行位置
                proc.pcb.setPC(testaddress);
//                设置pcb指令执行结束位置  但是为什么是  testaddress + instructions.length * 10 - 1  ？
                proc.pcb.setEndpointer(testaddress + instructions.length * 10 - 1);
//                存储指令到内存的instructionArea指令区
                MemoryDispatcher.storeInstruction(instructions, proc.pcb);
//                设置运行时长，一条指令耗时1
                proc.pcb.setRuntime(instructions.length * 1.0);
//                剩余时长，一条指令耗时1
                proc.pcb.setResttime(instructions.length * 1.0);
                proc.pcb.setName(name);
//                设置PCB长度为指令个数，一个指令1
                proc.pcb.setLength(instructions.length);
//            如果是闲逛进程
                if (name.equals("闲逛进程")) {
                    return proc;
                }
//                加入内存记录块组
                Memory.MAT.add(
                        new RecordBlock(proc.pcb.nameProperty(), proc.pcb.getStartpointer(), proc.pcb.getEndpointer()));
                return proc;
            }

        }

    }

//    转化多条指令
    public static int parseCommand(String instructions, String name) {
        ArrayList<String> instructionList = new ArrayList<>();
        String instruction = new String();
        System.out.println("随机生成，要转化的指令-->"+instructions);
        if (instructions.equals("")) {
            System.out.println("随机生成，要转化的指令为空");
            return 0;
        }
//        遍历指令
        for (int i = 0; i < instructions.length(); i++) {
//            如果是特殊标志，下一个
            if (instructions.charAt(i) == ';' || instructions.charAt(i) == ' ' || instructions.charAt(i) == '\n') {
                continue;
            } else {
//                正常字符，附加到instruction
                instruction += instructions.charAt(i);
            }
            if (instruction.length() == 3) {
//                如果长度到达3，加进instructionList
                instructionList.add(instruction);
//                如果是结束，break
                if (instruction.equals("end")){
                    break;
                }
//                重置
                instruction="";
            }

        }
        System.out.println(instructionList.size());
        if (!"end".equals(instructionList.get(instructionList.size() - 1))) {
            System.out.println("程序未以end结束");
            return 1;
        }
//        创建名字为name的pcb  加入到就绪队列，绑定要执行的指令
        ProcessDispatcher.getReadyProcessList().add(
//                创建进程拿到pcb
                Process.createProcess(instructionList.toArray(new String[instructionList.size()]), name)
                        .getPcb());
//        前端同步展示
        MemoryDispatcher.updateUI();
        ProcessDispatcher.showresources();
        return 2;
    }

    private static void setVariableArea(String[] program, Map<Character, Integer> VariableArea) {
//        遍历指令，找出变量和值
        for (String str : program) {
//            如果是赋值语句
            if (str.charAt(1) == '=') {
//                存变量和值进变量表
                VariableArea.put(new Character(str.charAt(0)), Integer.valueOf(str.substring(2)));
            }
        }
    }

//    随机创建进程
    public static void randomCreateProcess() {
        int num = 3;
        while (num > 0) {
//            随机设置进程名字
            String name = randomCreatename();
//            生成指令
            String generateInstructions = ExecutionFileGenerator.generateInstructions();
            System.out.println("generateInstructions=====>"+generateInstructions);
            parseCommand(generateInstructions, name);
            num--;
        }
        return;

    }

    //需要重写
    public static void randomIputProcessFile(){
        int num = 3;
        while (num > 0) {
//            随机设置进程名字
            String name = randomCreatename();
//            生成指令
            String generateInstructions = ExecutionFileGenerator.generateInstructions();
            System.out.println("generateInstructions=====>"+generateInstructions);
            parseCommand(generateInstructions, name);
            num--;
        }
        return;

    }


//    随机创建进程名字
    private static String randomCreatename() {
        int flag = -1;
        Random ra = new Random();
        String name = new String("P");
//        循环直到flag不为0
        while (flag != 0) {
            name = "p";
//            p+随机一个0~10数字
            name += (String.valueOf(ra.nextInt(10)));
//            flag置0
            flag = 0;
//            如果有就绪进程
            if (ProcessDispatcher.getReadyProcessList().size() != 0) {
//                遍历就绪进程
                for (PCB pcb : ProcessDispatcher.getReadyProcessList()) {
//                    如果就绪进程和这个名字相同，flag+1
                    if (pcb.getName().equals(name)) {
                        flag++;
                    }
                }
            } else {
//                如果就绪进程个数为0，直接break
                break;
            }
        }
        return name;
    }

    public PCB getPcb() {
        return pcb;
    }

}
